package com.wh1200.mybatis.xmlless

import cn.hutool.core.util.ReflectUtil
import com.wh1200.mybatis.xmlless.annotations.JoinObject
import com.wh1200.mybatis.xmlless.model.*
import com.wh1200.mybatis.xmlless.model.base.BaseClassWrapper
import com.wh1200.mybatis.xmlless.model.dto.DefaultFieldWrapper
import com.wh1200.mybatis.xmlless.model.dto.JavaFieldJoinObjectProvider
import com.wh1200.mybatis.xmlless.model.exception.XmlLessException
import com.wh1200.mybatis.xmlless.resolver.MappingResolver
import com.wh1200.mybatis.xmlless.resolver.ParameterResolver
import com.wh1200.mybatis.xmlless.resolver.TypeResolver
import com.wh1200.mybatis.xmlless.util.*
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo
import com.baomidou.mybatisplus.core.metadata.TableInfo
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils

/**
 *
 * @author 吴昊
 * @date 2024/1/19 9:39
 * @since v4.0.0
 * @version 1.0
 */
class JavaClassWrapper(private val clazz: Class<*>) : BaseClassWrapper {

  override fun isComplex(): Boolean {
    return ParameterResolver.isComplexType(clazz)
  }

  override fun getAllSuperClassesOrInterfaces(): List<String> {
    return ClassUtils.getAllClassOrInterface(clazz).map { it.name }
  }

  override fun isArray(): Boolean {
    return clazz.isArray
  }

  override fun getAllFields(): List<AnnotatedElementDetailInfo> {
    return if (this.isComplex()) {
      ReflectUtil.getFields(clazz).toList().map {
        ElementInfoBuilder.build(
            DefaultFieldWrapper(it)
        )
      }
    } else {
      listOf()
    }
  }

  override fun isCollection(): Boolean {
    return Collection::class.java.isAssignableFrom(clazz)
  }

  override fun getRealType(): BaseClassWrapper {
    return JavaClassWrapper(TypeResolver.resolveRealType(clazz))
  }

  override fun isString(): Boolean {
    return clazz == String::class.java
  }

  override fun isPrimitive(): Boolean {
    return clazz.isPrimitive
  }

  override fun getJavaClass(): Class<*> {
    return clazz
  }

  override fun getClassName(): String {
    return clazz.name
  }

  override fun initXmllessTableInfo(): XmllessTableInfo {
    val tableInfo = if (TableInfoHelper.getTableInfo(clazz) == null) {
      TableInfoHelper.initTableInfo(BuilderAssistantHolder.get(), clazz)
    } else {
      TableInfoHelper.getTableInfo(clazz)
    }
    val fieldList = tableInfo.fieldList.map {
      it.toXmllessTableField()
    }
    if (fieldList.count { it.isLogicDelete } > 1) {
      throw XmlLessException("实体类${tableInfo.entityType.name}中存在多个逻辑删除字段")
    }
    val xmllessTableInfo = XmllessTableInfo(
        tableInfo.keyProperty,
        tableInfo.keyColumn,
        tableInfo.tableName,
        getClassName(),
        fieldList,
        tableInfo.getFieldInfoMap(tableInfo.entityType, fieldList),
        fieldList.find { it.property == tableInfo.logicDeleteFieldInfo?.field?.name },
        MappingResolver.resolveFields(clazz).map {
          DefaultFieldWrapper(it)
        }.map {
          ElementInfoBuilder.build(it)
        }
    )
    return xmllessTableInfo
  }

  private fun TableFieldInfo.toXmllessTableField(): XmllessTableFieldInfo {
    val fieldWrapper = PlusTableFieldWrapper(this)
    val realType: BaseClassWrapper = ClassWrapperProvider.createClassWrapper(
        TypeResolver.resolveRealType(
            field.genericType
        )
    )
    val genericType = TypeInfo(field.genericType, realType)
    val selectedProperties = fieldWrapper.getSelectedProperties()
    val joinObject = AnnotationUtil.resolve<JoinObject>(field)?.let {
      JavaFieldJoinObjectProvider(
          it, selectedProperties,
          fieldWrapper.getFieldName(),
          genericType
      )
    }
    return XmllessTableFieldInfo(
        fieldWrapper,
        joinObject,
        genericType
    )
  }

  /**
   *
   * @param modelClass
   * @return
   */
  private fun TableInfo.getFieldInfoMap(
      modelClass: Class<*>,
      fieldList: List<XmllessTableFieldInfo>
  ): Map<String, XmllessTableFieldInfo> {
    val fieldInfoMap = fieldList.associateBy {
      it.property
    }.toMutableMap()
    if (!fieldInfoMap.containsKey(this.keyProperty)) {
      val fields = MappingResolver.resolveFields(modelClass)
      fields.firstOrNull { it.name == this.keyProperty }?.also {
        fieldInfoMap[this.keyProperty!!] = TableFieldInfo(
            GlobalConfigUtils.defaults(), this, it,
            this.reflector, TableInfoHelper.isExistTableLogic(modelClass, fields)
        ).toXmllessTableField()
      }
    }
    return fieldInfoMap
  }

}
